package com.piece.core.framework.util.collection;

import com.google.common.collect.ImmutableMap;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class CollectionUtil {

    public static boolean isAnyEmpty(Collection<?> collections) {
        if (null == collections || collections.size() == 0) {
            return true;
        }
        return collections.stream().anyMatch(ObjectUtil::isEmpty);
    }

    public static boolean isEmpty(Collection<?> collections) {
        if (null == collections || collections.size() == 0) {
            return true;
        }
        return false;
    }

    public static boolean isNotEmpty(Collection<?> collections) {
        return !isAnyEmpty(collections);
    }

    public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
        if (isAnyEmpty(from)) {
            return new ArrayList<>();
        }
        return from.stream().filter(predicate).collect(Collectors.toList());
    }

    public static boolean containsAny(Object source, Object... targets) {
        return Arrays.asList(targets).contains(source);
    }

    public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
        return org.springframework.util.CollectionUtils.containsAny(source, candidates);
    }

    public static <T> Collection<T> singleton(T entity) {
        return null == entity ? Collections.emptyList() : Collections.singleton(entity);
    }

    public static <T> T getFirst(Collection<T> from) {
        return ArrayUtil.isNotEmpty(from) ? from.iterator().next() : null;
    }

    public static <T> T getFirst(Collection<T> from, Predicate<T> predicate) {
        if (ArrayUtil.isEmpty(from)) {
            return null;
        }

        return from.stream().filter(predicate).findFirst().orElse(null);
    }

    public static <T> T get(Collection<T> from, int index) {
        if (ArrayUtil.isEmpty(from)) {
            return null;
        }

        int size = from.size();
        if (0 == size) {
            return null;
        } else {
            if (index < 0) {
                index += size;
            }

            if (index < size && index >= 0) {
                if (from instanceof List) {
                    List<T> list = (List)from;
                    return list.get(index);
                } else {
                    int i = 0;
                    for(Iterator var4 = from.iterator(); var4.hasNext(); ++i) {
                        T t = (T) var4.next();
                        if (i > index) {
                            break;
                        }

                        if (i == index) {
                            return t;
                        }
                    }

                    return null;
                }
            } else {
                return null;
            }
        }
    }

    public static <T, V extends Comparable<? super V>> V getMax(List<T> from, Function<T, V> valueFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return null;
        }
        // 断言，避免告警
        assert from.size() > 0;
        T t = from.stream().max(Comparator.comparing(valueFunc)).get();
        return valueFunc.apply(t);
    }

    public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {
        if (ArrayUtil.isEmpty(from)) {
            return new ArrayList<>();
        }
        return distinct(from, keyMapper, (t1, t2) -> t1);
    }

    public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {
        if (ArrayUtil.isEmpty(from)) {
            return new ArrayList<>();
        }
        return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
    }

    public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
        return convertList(from, func, true);
    }

    public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, boolean ignoreNull) {
        if (ArrayUtil.isEmpty(from)) {
            return new ArrayList<>();
        }

        if (ignoreNull) {
            return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
        }
        return from.stream().map(func).collect(Collectors.toList());
    }

    public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashSet<>();
        }
        return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
    }

    public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return convertMap(from, keyFunc, Function.identity());
    }

    public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
        if (ArrayUtil.isEmpty(from)) {
            return supplier.get();
        }
        return convertMap(from, keyFunc, Function.identity(), supplier);
    }

    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
    }

    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
    }

    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
        if (ArrayUtil.isEmpty(from)) {
            return supplier.get();
        }
        return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
    }

    public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
    }

    public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
    }

    public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return from.stream()
                .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
    }

    public static <T, K, V> Map<K, Set<V>> convertMultiMapSet(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return new HashMap<>();
        }
        return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
    }

    public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {
        if (ArrayUtil.isEmpty(from)) {
            return Collections.emptyMap();
        }
        ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();
        from.forEach(item -> builder.put(keyFunc.apply(item), item));
        return builder.build();
    }

    public static <T> List<Map<String, Object>> convertListMap(Collection<T> from) {
        return convertListMap(from, null);
    }

    public static <T> List<Map<String, Object>> convertListMap(Collection<T> from, Predicate predicate) {
        List<Map<String, Object>> result = new ArrayList<>();

        if (ArrayUtil.isEmpty(from)) {
            return result;
        }

        Stream<T> stream = from.stream();
        if (null != predicate) {
            stream = stream.filter(predicate);
        }

        stream.forEach(item -> {
            Map map = new HashMap();
            BeanUtil.beanToMap(item, map);
            result.add(map);
        });
        return result;
    }

    public static String convertString(Collection from, String split) {
        return convertString(from, null, split);
    }

    public static String convertString(Collection from, Function func, String split) {
        if (ArrayUtil.isEmpty(from)) {
            return "";
        }

        if (null != func) {
            return ArrayUtil.join(from.stream().map(func).filter(Objects::nonNull).toArray(), split);
        }

        return ArrayUtil.join(from.stream().filter(Objects::nonNull).toArray(), split);
    }

    public static <T> List<T> setOrAppend(List<T> list, int index, T element) {
        if (index < list.size()) {
            list.set(index, element);
        } else {
            list.add(element);
        }

        return list;
    }

    public static <T> List<T> sub(List<T> list, int start, int end) {
        return sub(list, start, end, 1);
    }

    public static <T> List<T> sub(List<T> list, int start, int end, int step) {
        if (null == list) {
            return null;
        } else if (list.isEmpty()) {
            return new ArrayList(0);
        } else {
            int size = list.size();
            if (start < 0) {
                start += size;
            }

            if (end < 0) {
                end += size;
            }

            if (start == size) {
                return new ArrayList(0);
            } else {
                if (start > end) {
                    int tmp = start;
                    start = end;
                    end = tmp;
                }

                if (end > size) {
                    if (start >= size) {
                        return new ArrayList(0);
                    }

                    end = size;
                }

                if (step < 1) {
                    step = 1;
                }

                List<T> result = new ArrayList();

                for(int i = start; i < end; i += step) {
                    result.add(list.get(i));
                }

                return result;
            }
        }
    }

    public static <T> void addIfNotNull(Collection<T> coll, T item) {
        if (null == item) {
            return;
        }
        coll.add(item);
    }
}
